/************************************************************************
 *   IRC - Internet Relay Chat, src/m_sacmds.c
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 1, or (at your option)
 *   any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "channel.h"
#include "msg.h"		  /* For TOK_*** and MSG_*** strings  */
#include "hash.h"		  /* For CHANNELHASHSIZE */
#include "h.h"

//Some systems dont have strlcat -_-
#ifndef strlcat
#define strlcat strncat
#endif

/*
   **
   ** This file holds the SA* commands
   ** Commands can be used by SO+
   ** Currently: SAMODE, SAJOIN, SAPART
   **
 */


/*
 * some buffers for rebuilding channel/nick lists with ,'s
 */
static char nickbuf[BUFSIZE], buf[BUFSIZE];
static char modebuf[MODEBUFLEN], parabuf[MODEBUFLEN];

/*
 * m_samode
 * parv[0] = sender
 * parv[1] = channel
 * parv[2] = modes
 * -taz
 */
int m_samode (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
/*  static char tmp[MODEBUFLEN]; Compiler says this is unused */
    int badop, sendts;
    aChannel *chptr;

    if (check_registered(cptr))
	return 0;

    if (!IsPrivileged(cptr)) {
		sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
		return 0;
    }
    if (!IsSAdmin(cptr) || parc < 2) {
		sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
		return 0;
	}

    chptr = find_channel(parv[1], NullChn);
    if (chptr == NullChn)
	return 0;

    sptr->flags &= ~FLAGS_TS8;

    clean_channelname (parv[1]);

    if (check_channelmask (sptr, cptr, parv[1]))
	return 0;

    sendts = set_mode (cptr, sptr, chptr, parc - 2, parv + 2,
		       modebuf, parabuf, &badop, 1);

    if (strlen (modebuf) > (size_t) 1 || sendts > 0) {
	if (badop != 2 && strlen (modebuf) > (size_t) 1)
	    sendto_channel_butserv (chptr, sptr, ":%s MODE %s %s %s",
				    me.name, chptr->chname, modebuf, parabuf);

	sendto_match_servs (chptr, cptr, ":%s MODE %s %s %s 1",
			    parv[0], chptr->chname, modebuf, parabuf);

	if (MyClient (sptr)) {
	    sendto_serv_butone (&me, ":%s GLOBOPS :%s used SAMODE (%s %s %s)",
				me.name, sptr->name, chptr->chname, modebuf,
				parabuf);
	    sendto_failops_whoare_opers ("from %s: %s used SAMODE (%s %s %s)",
					 me.name, sptr->name, chptr->chname,
					 modebuf, parabuf);
	}
    }
    return 0;
}


/*
   ** m_sajoin
   **
   ** This function allows a SO, SRA, or U:Line to force
   ** a nick to join a channel.  -- Kei
   **
   **   parv[0] = sender prefix
   **   parv[1] = nick
   **   parv[2] = channel to join
 */

int m_sajoin (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
	aClient *acptr;
	char jbuf[BUFSIZE];
	int zombie = 0;

	if (!IsSAdmin(sptr) && !IsULine(sptr, sptr))
	{
	 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
	 return 0;
	}

	if (parc < 3)
	{
	 sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SAJOIN");
	 return 0;
	}

	if (!(acptr = find_person(parv[1], NULL)))
	{
		sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], parv[1]);
		return 0;
	}
	if (MyClient(acptr))
	{
		char *name, *p = NULL;
		int i, parted = 0;

		*jbuf = 0;

		/* Now works like m_join */
		for (i = 0, name = strtoken(&p, parv[2], ","); name; name = strtoken(&p,
		     NULL, ","))
		{
			aChannel *chptr;
			Link *lp;

			if (strlen(name) > CHANNELLEN)
				name[CHANNELLEN] = 0;
			clean_channelname(name);
			if (*name == '0' && !atoi(name))
			{
				(void)strcpy(jbuf, "0");
				i = 1;
				parted = 1;
				continue;
			}
			if (check_channelmask(sptr, cptr, name) == -1 || *name == '0' ||
			    !IsChannelName(name))
			{
				sendto_one(sptr,
				    err_str(ERR_NOSUCHCHANNEL), me.name,
				    parv[0], name);
				continue;
			}

			chptr = get_channel(acptr, name, 0);
			if (!parted && chptr && (lp = find_user_link(chptr->members, acptr)))
			{
				sendto_one(sptr, err_str(ERR_USERONCHANNEL), me.name, parv[0],
					   parv[1], name);
				continue;
			}
			if (*jbuf)
				(void)strlcat(jbuf, ",", sizeof jbuf);
			(void)strlncat(jbuf, name, sizeof jbuf, sizeof(jbuf) - i - 1);
			i += strlen(name) + 1;
		}
		if (!*jbuf)
			return -1;
		i = 0;
		strcpy(parv[2], jbuf);
		*jbuf = 0;
		for (name = strtoken(&p, parv[2], ","); name; name = strtoken(&p, NULL, ","))
		{
			int flags;
			aChannel *chptr;
			Link *lp;

			if (*name == '0' && !atoi(name))
			{
				while ((lp = acptr->user->channel)) {
					chptr = lp->value.chptr;
					sendto_channel_butserv(chptr, acptr,
					    ":%s PART %s :%s", acptr->name, chptr->chname,
					    "Left all channels");
					remove_user_from_channel(acptr, chptr);
				}
				sendto_serv_butone(acptr, ":%s JOIN 0", acptr->name);
				strcpy(jbuf, "0");
				i = 1;
				continue;
			}
			flags = (ChannelExists(name)) ? CHFL_DEOPPED : CHFL_CHANOP;
			chptr = get_channel(acptr, name, CREATE);
			if (chptr && (lp = find_user_link (chptr->members, acptr))) {
			    if (lp->flags & CHFL_ZOMBIE) {
					zombie = 1;
					flags = lp->flags & (CHFL_DEOPPED | CHFL_SERVOPOK);
					remove_user_from_channel (acptr, chptr);
					chptr = get_channel (acptr, name, CREATE);
			    }
			    else
				continue;
			}

			/*
			**  Complete user entry to the new channel (if any)
			*/
			add_user_to_channel (chptr, acptr, flags);
			/*
			** notify all other users on the new channel
			*/
			sendto_channel_butserv (chptr, acptr, ":%s JOIN :%s", parv[1], name);
			sendto_match_servs (chptr, acptr, ":%s JOIN :%s", parv[1], name);

			if (MyClient (sptr)) {
			/*
			** Make a (temporal) creationtime, if someone joins
			** during a net.reconnect : between remote join and
			** the mode with TS. --Run
			*/
				if (chptr->creationtime == 0) {
					chptr->creationtime = time (NULL);
					sendto_match_servs (chptr, cptr, ":%s MODE %s + %lu",
						me.name, name, chptr->creationtime);
				}
				del_invite (acptr, chptr);
				if (flags & CHFL_CHANOP)
					sendto_match_servs (chptr, cptr,
					":%s MODE %s +o %s %lu",
					me.name, name, parv[0],
					chptr->creationtime);
				if (chptr->topic[0] != '\0') {
					sendto_one (acptr, rpl_str (RPL_TOPIC), me.name,
						parv[1], name, chptr->topic);
					sendto_one (acptr, rpl_str (RPL_TOPICWHOTIME),
						me.name, parv[1], name,
						chptr->topic_nick, chptr->topic_time);
				}
				parv[1] = name;
				acptr->flags |= FLAGS_JOINING;
				(void)m_names(acptr, acptr, 2, parv);
				acptr->flags &= ~FLAGS_JOINING;
			}
			if (*jbuf)
				(void)strlcat(jbuf, ",", sizeof jbuf);
			(void)strlncat(jbuf, name, sizeof jbuf, sizeof(jbuf) - i - 1);
			i += strlen(name) + 1;
		}
		sendto_one(acptr, ":%s NOTICE %s :*** You were forced to join %s",
			me.name, acptr->name, jbuf);
		sendto_realops("%s used SAJOIN to make %s join %s", sptr->name, acptr->name,
			       jbuf);
		sendto_serv_butone(&me, ":%s GLOBOPS :%s used SAJOIN to make %s join %s",
				   me.name, sptr->name, acptr->name, jbuf);
	}
	else
	{
		sendto_one(acptr, ":%s SAJOIN %s %s", parv[0],
		    parv[1], parv[2]);
	}

	return 0;
}

/*
   ** m_sapart
   **
   ** This function allows a SO, SRA, or U:Line to force
   ** a nick to part a channel.  -- Kei
   **
   **   parv[0] = sender prefix
   **   parv[1] = nick
   **   parv[2] = channel to part
   **	parv[3] = comment
 */
int m_sapart (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
	aClient *acptr;
	aChannel *chptr;
	Link *lp;
	char *comment = (parc > 3 && parv[3] ? parv[3] : NULL);
	char commentx[512];
	if (!IsSAdmin(sptr) && !IsULine(sptr, sptr))
	{
		sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
		return 0;
	}

	if (parc < 3)
	{
		sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SAPART");
		return 0;
	}

	if (!(acptr = find_person(parv[1], NULL)))
	{
		sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], parv[1]);
		return 0;
	}
	if (!(chptr = get_channel(acptr, parv[2], 0)))
	{
		sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0],
			parv[2]);
		return 0;
	}
	if (!(lp = find_user_link(chptr->members, acptr)))
	{
		sendto_one(sptr, err_str(ERR_USERNOTINCHANNEL), me.name, parv[0],
			   parv[1], parv[2]);
		return 0;
	}

	if (comment)
	{
		sendto_realops("%s used SAPART to make %s part %s (%s)", sptr->name, parv[1],
		               parv[2], comment);
		strcpy(commentx, "SAPart: ");
		strlcat(commentx, comment, 512);
	}
	else
	{
		sendto_realops("%s used SAPART to make %s part %s", sptr->name, parv[1],
			        parv[2]);
	}

	if (MyClient(acptr))
	{
		parv[0] = parv[1];
		parv[1] = parv[2];
		parv[2] = comment ? commentx : NULL;
		if (comment) {
			sendto_one(acptr,
			    ":%s NOTICE %s :*** You were forced to part %s (%s)", me.name,
			    acptr->name, parv[1], commentx);
		}
		else {
			sendto_one(acptr,
			    ":%s NOTICE %s :*** You were forced to part %s", me.name,
			    acptr->name, parv[1]);
		}
		m_part (&me, acptr, parc - 1, &parv[0]);
	}
	else
	{
		if (comment)
			sendto_one(acptr, ":%s SAPART %s %s :%s", parv[0],
			    parv[1], parv[2], comment);
		else
			sendto_one(acptr, ":%s SAPART %s %s", parv[0], parv[1],
				   parv[2]);
	}

	return 0;
}

